﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.IO;
using System.Threading;
using System.Text.RegularExpressions;
using IGolovkine.Core;
using IGolovkine.Core.Diagnostics;

namespace IGolovkine.ShoutCastRipper
{
    public class StationClient : IStationClient
    {
        public event EventHandler<ContentEndEventArgs> ContentEnd;
        public event EventHandler<ContentStartEventArgs> ContentStart;
        public event EventHandler<DisconnectedEventArgs> Disconnected;
        public event EventHandler<ContentReceivedEventArgs> ContentReceived;

        private readonly TypeFactory<IStationConnection> _connectionFactory;
        private string _currentTitle;

        private DateTime _connectionTime = DateTime.MaxValue;
        private DateTime _currentTitleStartTime = DateTime.MaxValue;
        private IStationConnection _connection;
        private Thread _recordingThread;

        private readonly ObjectDisposedCheck _disposedCheck;

        public StationClient(TypeFactory<IStationConnection> connectionFactory)
        {
            Debug.NotDefault(() => connectionFactory);
            _connectionFactory = connectionFactory;
            _recordingThread = null;
            _disposedCheck = new ObjectDisposedCheck(this);
        }

        public bool IsConnected
        {
            get { return _connection != null; }
        }

        public void Connect()
        {
            _disposedCheck.EnsureNotDisposed();
            if (this.IsConnected)
            {
                throw new InvalidOperationException("StationListener is already connected");
            }
            else
            {
                _connection = this._connectionFactory();
                _connectionTime = DateTime.Now;
                _recordingThread = new Thread(this.Record);
                _recordingThread.Name = "Recording Thread";
                _recordingThread.Start();
            }
        }

        public void Disconnect()
        {
            if (this.IsConnected)
            {
                _connection.Close();
                _connection = null;
             
                if (Thread.CurrentThread != _recordingThread)
                {
                    _recordingThread.Join();
                }
                
                _recordingThread = null;
            }
            else
            {
                throw new InvalidOperationException("StationListener is already disconnected");
            }
        }

        private void Record()
        {
            this.OnDisconnected(this.ReadContent());
        }

        public string CurrentTitle
        {
            get { return _currentTitle; }
        }

        public DateTime CurrentTitleStartTime
        {
            get { return _currentTitleStartTime; }
        }

        private TimeSpan GetCurrentTitleLength()
        {
            if (_currentTitleStartTime != DateTime.MaxValue)
            {
                return DateTime.Now - _currentTitleStartTime;
            }
            else
            {
                return TimeSpan.Zero;
            }
        }

        public DateTime ConnectionTime
        {
            get { return _connectionTime; }
        }

        private DisconnectedEventArgs ReadContent()
        {
            _disposedCheck.EnsureNotDisposed();

            try
            {
                while (this.IsConnected)
                {
                    byte[] data = _connection.ReadContentData();                    
                    if (this.IsConnected && _connection.CurrentContentTitle != _currentTitle)
                    {
                        if (_currentTitle != null)
                        {
                            ContentEndEventArgs contentEndArgs = new ContentEndEventArgs(_currentTitle, this.GetCurrentTitleLength());
                            this.OnContentEnd(contentEndArgs);
                        }

                        _currentTitleStartTime = DateTime.Now;
                        _currentTitle = _connection.CurrentContentTitle;

                        if (this.IsConnected)
                        {
                            ContentStartEventArgs contentStartArgs = new ContentStartEventArgs(_connection.CurrentContentTitle);
                            this.OnStartContent(contentStartArgs);
                        }
                    }

                    if (this.IsConnected)
                    {
                        ContentReceivedEventArgs dataArgs = new ContentReceivedEventArgs(data, _currentTitle, this.GetCurrentTitleLength());
                        this.OnDataReceived(dataArgs);
                    }
                }
                return new DisconnectedEventArgs(_currentTitle, this.GetCurrentTitleLength());
            }
            catch (Exception ex)
            {
                return new DisconnectedEventArgs(_currentTitle, this.GetCurrentTitleLength(), ex);
            }
            finally
            {
                this.OnContentEnd(new ContentEndEventArgs(_currentTitle, this.GetCurrentTitleLength()));
                if (this.IsConnected)
                {
                    this.Disconnect();
                }
                _currentTitleStartTime = DateTime.MaxValue;
                _connectionTime = DateTime.MaxValue;
                _currentTitle = null; 
            }
        }

        protected virtual void OnStartContent(ContentStartEventArgs args)
        {
            if (this.ContentStart != null)
            {
                this.ContentStart(this, args);
            }
        }

        protected virtual void OnContentEnd(ContentEndEventArgs args)
        {
            if (this.ContentEnd != null)
            {
                this.ContentEnd(this, args);
            }
        }

        protected virtual void OnDisconnected(DisconnectedEventArgs args)
        {
            if (this.Disconnected != null)
            {
                this.Disconnected(this, args);
            }
        }

        protected virtual void OnDataReceived(ContentReceivedEventArgs args)
        {
            if (this.ContentReceived != null)
            {
                this.ContentReceived(this, args);
            }
        }

        public void Dispose()
        {
            _disposedCheck.Dispose();
            if (this.IsConnected)
            {
                this.Disconnect();
            }
        }
    }
}
